package com.zh.webcommon.common.param.secure;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import java.io.IOException;

public class PrivacyMaskSerializer extends JsonSerializer<String> implements ContextualSerializer {
    private MaskFieldEnum maskFieldType;
    private int prefixNoMaskLen;
    private int suffixNoMaskLen;

    public PrivacyMaskSerializer() {
        //必须要有空constructor
    }

    public PrivacyMaskSerializer(MaskFieldEnum maskFieldType, int prefixNoMaskLen, int suffixNoMaskLen) {
        if (maskFieldType == MaskFieldEnum.CUSTOM) {
            this.prefixNoMaskLen = prefixNoMaskLen;
            this.suffixNoMaskLen = suffixNoMaskLen;
        }
        this.maskFieldType = maskFieldType;
    }

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        switch (maskFieldType) {
            case EMAIL -> {
                gen.writeString(MaskUtil.maskEmail(value));
            }
            case PHONE -> {
                gen.writeString(MaskUtil.maskPhone(value));
            }
            case ID_CARD -> {
                gen.writeString(MaskUtil.maskIdCard(value));
            }
            case BANK_CARD -> {
                gen.writeString(MaskUtil.maskBankCard(value));
            }
            case CUSTOM -> {
                gen.writeString(MaskUtil.doMaskKeepFirstAndLast(value, prefixNoMaskLen, suffixNoMaskLen));
            }
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        // 为空直接跳过
        if (property == null)
            return prov.findNullValueSerializer(null);

        // 只处理String类型
        if (property.getType().getRawClass() == String.class) {
            var annotation = property.getAnnotation(PrivacyMask.class);
            if (annotation == null) {
                annotation = property.getContextAnnotation(PrivacyMask.class);
            }
            if (annotation != null) {
                return new PrivacyMaskSerializer(annotation.type(), annotation.prefixNoMaskLen(), annotation.suffixNoMaskLen());
            }
        }

        return prov.findValueSerializer(property.getType(), property);
    }

    public static class MaskUtil {
        public static String maskEmail(String value) {
            int index = value.indexOf("@");
            if (index < 1) return value;

            return doMaskKeepFirstAndLast(value, 3, value.length() - index);
        }

        public static String maskPhone(String value) {
            return doMaskKeepFirstAndLast(value, 3, 3);
        }

        public static String maskIdCard(String value) {
            return doMaskKeepFirstAndLast(value, 4, 4);
        }

        public static String maskBankCard(String value) {
            return doMaskKeepFirstAndLast(value, 5, 4);
        }

        protected static String doMaskKeepFirstAndLast(String value, int prefixNoMaskLen, int suffixNoMaskLen) {
            if ((prefixNoMaskLen == -1 && suffixNoMaskLen == -1)) return value;
            if (prefixNoMaskLen < 0) prefixNoMaskLen = 0;
            if (suffixNoMaskLen < 0) suffixNoMaskLen = 0;
            if (prefixNoMaskLen == 0 && suffixNoMaskLen == 0) return "******";

            int maskLen = value.length() - (prefixNoMaskLen + suffixNoMaskLen);
            if (maskLen <= 0) {
                return value;
            }
            var format = "0".repeat(prefixNoMaskLen) + "*".repeat(maskLen) + "0".repeat(suffixNoMaskLen);

            return decode(value, format);
        }

        private static String decode(String value, String format) {
            char[] chars = value.toCharArray();
            char[] maskChars = format.toCharArray();

            for (int i = 0, j = 0; i < maskChars.length; i++) {
                if ('0' == maskChars[i]) {
                    maskChars[i] = chars[j++];
                } else if ('*' == maskChars[i]) {
                    j++;
                } else {
                    while (j < chars.length) {
                        if (chars[j++] == maskChars[i]) {
                            break;
                        }
                    }
                }
            }
            return new String(maskChars);
        }
    }
}